﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AzureGames.Data;
using AzureGames.Model.Statistics;
using Model = AzureGames.Model;
using AzureGames.Domain.Models;
using AzureGames.Domain.Contracts;
using AzureGames.Model;

namespace AzureGames.Domain.Handlers
{
    /// <summary>
    /// Register to play a game
    /// </summary>
    /// <remarks>this is used to either start a new game or join a game waiting for players</remarks>
    public class RegisterNewGame
    {
        private readonly IBoardRepository _boardRepository;
        private readonly IGameDefinitionRepository _gameDefintionRepository;
        private readonly INotificationRepository _notificationRepository;
        private readonly IPlayerRepository _playerRepository;
        private readonly IUserStatisticsRepository _userstatisticsRepository;
        private readonly IGameStatisticsRepository _gamestatisticsRepository;

        public RegisterNewGame(IBoardRepository gameBoardRepository, IGameDefinitionRepository gameDefinitionRepository, INotificationRepository notificationRepository, IPlayerRepository playerRepository, IUserStatisticsRepository userstatisticsRepository, IGameStatisticsRepository gamestatisticsRepository)
        {
            _boardRepository = gameBoardRepository;
            _gameDefintionRepository = gameDefinitionRepository;
            _notificationRepository = notificationRepository;
            _playerRepository = playerRepository;
            _gamestatisticsRepository = gamestatisticsRepository;
            _userstatisticsRepository = userstatisticsRepository;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameDefinitionID"></param>
        /// <param name="playerID"></param>
        /// <returns></returns>
        public virtual GameBoard Execute(int gameDefinitionID, string currentPlayerID)
        {
            try
            {
                var gameDefinition = _gameDefintionRepository.GetGameDefinition(gameDefinitionID);

                // this will join or create a new game
                bool wasJoined;
                Board board = _boardRepository.RegisterNewGame(gameDefinition, currentPlayerID, out wasJoined);

                // statistics 
                if (wasJoined)
                {
                    _userstatisticsRepository.AddUserStatistic(gameDefinitionID, GameType.MultiplePlayer, DateTime.Now.Date, currentPlayerID, Board.Created);
                }
                else
                {
                    _gamestatisticsRepository.AddGameStatistic(gameDefinitionID, GameType.MultiplePlayer, DateTime.Now.Date, Board.Created);
                    _userstatisticsRepository.AddUserStatistic(gameDefinitionID, GameType.MultiplePlayer, DateTime.Now.Date, currentPlayerID, Board.Created);
                }

                var players = new List<Model.Player>();
                foreach (var playerID in board.PlayerIDs)
                {
                    players.Add(_playerRepository.GetPlayer(playerID));
                }
                
                var gamePlayers = new List<GamePlayer>(players.Select(p => new GamePlayer(p)));

                foreach (var player in players)
                {
                    if (player is User && player.ID != currentPlayerID)
                    {
                        // notify players of change by adding message to queue
                        _notificationRepository.PostNotification(new Notification
                        {
                            GameDefinitionID = board.GameDefinitionID,                            
                            ID = Guid.NewGuid(),
                            PlayerID = player.ID,
                            BoardID = board.ID,
                            Data = board,
                            Message = "UpdateBoard",
                            State = Notification.STATE.Pending,
                        });
                    }
                }

                return new GameBoard(gameDefinition, board, new List<GamePlayer>(players.Select(player => new GamePlayer(player))));
            }
            catch (InvalidOperationException e)
            {
                throw new BusinessServicesException("Unable to retrieve game board from database.", e);
            }
        }
    }
}
